/*! \file 
	\brief Holds the implementation of the memory sub-system. */

#include "kernel.h"
#include "mm.h"

#define INT_STRINGIFY(x)	#x
#define INT_TOSTRING(x)		INT_STRINGIFY(x)
#define FILE_AND_LINE		__FILE__ ":" INT_TOSTRING(__LINE__)	

#define TRACE(x)	{kprints("TRACE: " FILE_AND_LINE ":\t " #x "\n");}
#define DEBUG(x)	{kprints("DEBUG: " FILE_AND_LINE ":\t (" #x ") = 0x"); kprinthex((long)(x)); kprints("\n");}
#define FAIL(x)		{kprints("FAIL:  " FILE_AND_LINE ":\t (" #x ") = 0x"); kprinthex((long)(x));}

#define ENSURE(x,code)			{ if (!(x)) {FAIL(x); kprints("\n"); code} }
#define MENSURE(x,msg,code)		{ if (!(x)) {FAIL(x); kprints(" Reason: " msg);  kprints("\n"); code} }

/* Variable definitions. */

struct page_frame
page_frame_table[MAX_NUMBER_OF_FRAMES];

/* Set when the system is initialized. */
unsigned long memory_pages;

/* The following three variables are set by the assembly code. */
unsigned long first_available_memory_byte;

const unsigned long memory_size;

const unsigned long kernel_page_table_root;

/* Function definitions. */

/* Change this function in assignment 4. */
extern long
kalloc(const register unsigned long length,
		const register unsigned int process,
		const register unsigned long flags) 
{
	MENSURE(length > 0, "Can not allocate zero or negative amount of memory",{return ERROR;});

	int requested_pages = (length + 4095)>>12;	// round up to integer page number
	const int max_page = memory_pages-1;		// memory_pages is a count, make it 0-based

	int cnt = 0;
	int page_num;		// Page number of first page in the allocated block of pages

	/* Similar to first fit (just on bitmaps) [Tanenbaum p.184].
	 * Starting from top mem, going down until first fit */
	for (page_num = max_page; page_num >= 0; page_num--) {
		if (page_frame_table[page_num].owner == -1) {
			cnt++;
		} else {
			cnt = 0;
		}

		if (cnt == requested_pages) {
			goto found;
		}
	}

	/* We did not find the requested number of pages. */
	return ERROR;

	
found:	
	/* We did find the requested number of pages. 
	 * Since we  */

	ENSURE(cnt == requested_pages, {return ERROR;});
	ENSURE(page_num >= 0, {return ERROR;});

	int i;
	for (i = 0; i < requested_pages; i++) {
		page_frame_table[page_num + i].owner = process;
		page_frame_table[page_num + i].start = page_num;
		page_frame_table[page_num + i].free_is_allowed = !(flags & ALLOCATE_FLAG_KERNEL);
	}

	return page_num << 12;	// Return byte-address for the found page
}

/* Change this function in assignment 4. */
long
kfree(const register unsigned long address) 
{
	typedef int bool;	// C has no bool built-in. bools convey semantics better than int's
	
	const int page_num = address>>12;	// 'address' is byte-addressed; we need the page number
	const int pid = thread_table[cpu_private_data.thread_index].data.owner;

	/* Get the page which contains 'address' */
	struct page_frame* page = &page_frame_table[page_num];

	const bool is_owner = (pid == page->owner);			// Must not allow any procsess to free any other proceses' memory
	const bool is_start = (page->start == page_num);	// 'address' must point to beginning of block (though not needed in bitmapped implementation)
	const bool okay = (is_owner && is_start && page->free_is_allowed);	// Obey the free_is_allowed flag

	if (!okay)
		return ERROR;

	/* We are okay now, proceed to free all pages in allocated block */
	while (page->start == page_num)	// Free only pages belonging to this block (ie. all those pages saying belong to the block starting at page page_num)
	{
		page->owner = -1;			// Nobody owns the page now
		page->start = -1;			// It does not belong to any memory block
		page->free_is_allowed = 0;	// And don't allow double free()ing
		page++;	// goto next page 
	}

	return ALL_OK;
}

/* Implementing this fuction is not part of assignment 4. */
extern void
update_memory_protection(const register unsigned long page_table,
		const register unsigned long start_address,
		const register unsigned long length,
		const register unsigned long flags) {
}

/* Implementing this fuction is not part of assignment 4. */
extern void
initialize_memory_protection() {
}

/* Put any code you need to add here. */
